home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Frameworks / Dragonsmith 1.1.1 / CW 4.5 Update / Dragon.cp < prev    next >
Encoding:
Text File  |  1994-07-04  |  37.5 KB  |  1,127 lines  |  [TEXT/MMCC]

  1. /*
  2.     Dragon.c
  3.     
  4.     Dragonsmith 1.1.1
  5.     
  6.     Simple framework for a drag-and-drop application.  A "dragon" does something meaningful (one
  7.     would hope) with the Finder objects that were drag-and-dropped onto it.  Most dragons will
  8.     stick around after the initial launch unless there are no settings for the user to change.
  9.     
  10.     This class does nothing by itself; simple subclasses will need to override ProcessFile —
  11.     more powerful subclasses may need to do much more (e.g., override event handling methods).
  12.     
  13.     Developed using THINK C 5.0 and 6.0, Resorcerer 1.1.1 and ResEdit 2.1.1
  14.     
  15.   Modified: 01 Jul 1994 by Francis H Schiffer, 3rd - updated for Universal Headers and
  16.                             fixed calling sequence for SuspendAEvent
  17.  
  18.     Copyright © 1992–1994 by Paul M. Hoffman
  19.     Send comments or suggestions to paul.hoffman@umich.edu -or- dragonsmith@umich.edu
  20.     
  21.     This source code may be freely used, altered, and distributed in any way as long as:
  22.         1.    It is GIVEN away rather than sold (except as expressly permitted by the author)
  23.         2.    This statement and the above copyright notice are left intact.
  24.     
  25.     For a complete description of the contents of this file, see the "Dragonsmith Programmer’s Manual"
  26.     
  27. */
  28.  
  29. #include    "Dragon.h"
  30.  
  31. #include    <Folders.h>
  32. #include    "AppleEventUtils.h"
  33. #include    "EventUtils.h"
  34. #include    "GestaltUtils.h"
  35. #include    "HandleUtils.h"
  36. #include    "MenuUtils.h"
  37. #include    "StringUtils.h"
  38. #include    "ProcessUtils.h"
  39. #include    "TrapUtils.h"
  40.  
  41. //    -------------------------------------------    P u b l i c     m e t h o d s    ----------------------------------------------
  42.  
  43. Dragon::Dragon (void)
  44. {
  45.     FSSpec    appFile;
  46.     Handle    h;
  47.     
  48.     // We use unrelocatable blocks for curDocPB and curDocFSS in order to avoid having to HLock the dragon object
  49.     //    to prevent its instance variables from moving during calls to (for example) ResolveAliasFile, etc.
  50.     
  51.     curDocFSS = (FSSpec *) NewPtr (sizeof (FSSpec));
  52. // NOTE bug-fix 1.1.1 — call NewPtrClear here instead of NewPtr to clear unused parameters
  53.     curDocPB = (PBRecUnion *) NewPtrClear (sizeof (PBRecUnion));    
  54.     if (curDocPB == NULL || curDocFSS == NULL)
  55.         Abort (memFullErr);
  56.         
  57.     curDocPB->h.fileParam.ioCompletion = NULL;            // All PB calls will be synchronous
  58.     curDocPB->h.fileParam.ioNamePtr = curDocFSS->name;    // This only has to be set once, since curDocFSS won't move
  59.     curDocFSS->name[0] = 0;    // Just in case
  60.  
  61.     dirDepthLimit = 0;            // Change this instance variable to a largish negative number (say, -100) in your
  62.                             //    dragon's constructor if you want to recursively open all folders and volumes to
  63.                             //    process what's inside.  Or change it to -1 if you just want to go down 1 level
  64.     curDirDepth = 0;            // Start out at the top level of processing (of course)
  65.     filesOnly = FALSE;            // If we get FSSpecs to folders to volumes, go ahead and use them as is
  66.                             //    — set this to TRUE if dirDepthLimit contains a small negative value (i.e., a value
  67.                             //    near zero) and your ProcessDroppings is designed to work only on files
  68.                             // If you don't want to get folders and volumes at all, just say "no" — don't put
  69.                             //    'fold' and 'disk' in the FREF
  70.     resolveAliases = TRUE;        // Resolve any aliases you end up getting? (i.e., by opening folders — the Finder
  71.                             //    kindly resolves any aliases that are dragged-and-dropped, so this instance
  72.                             //    variable is meaningless unless dirDepthLimit < 0)
  73.     followAliasChain = TRUE;    // Always resolve aliases to the original file (not to an intermediate alias)?
  74.     useCustomFilter = FALSE;    // Don't do any special filtering of docs
  75.     acceptableTypes = NULL;    // The types of documents which we can process (e.g., 'TEXT', '****', 'disk', etc.)
  76.     
  77.     preferences = NULL;
  78.     prefsFileType = kPrefsFileType;    // Subclasses should probably not change this — the System 7 Finder displays
  79.                                 //    a rather nice icon for files of this type
  80.     dragonPrefs = NULL;
  81.     
  82.     appResFork = CurResFile ();    // Keep a refNum to the application's resource fork
  83.     
  84.     // Now use it to get an FSSpec to our app's file — we use a local copy because calling RefNumToFSSpec 
  85.     //    may move memory
  86.     if (RefNumToFSSpec (appResFork, &appFile, NULL) != noErr)
  87.         appFile.name[0] = 0;        // This should never never be executed, but then you never never know…
  88.     this->appFile = appFile;            // Copy local variable to instance variable
  89.     
  90.     h = Get1Resource ('BNDL', 128);
  91.     signature = (h ? **((OSType **) h) : '????');        // '????' is the least obnoxious default I could think of
  92.  
  93.     acceptableTypes = FREFTypes (appResFork);    // File types this dragon will accept (from its 'FREF' resources)
  94.  
  95.     // These hard-coded default sleep values will be overridden by ones from the Dragon preferences resource
  96.     //    ('DrPr' 128) — if everything goes well in InitPrefs, that is
  97.     sleepValue[kFGIdle] = 14;                    // Allow TEIdle to work OK (15 would PROBABLY be safe, too)
  98.     sleepValue[kBGIdle] = 60 * 60;                // Laziest state — check once every minute
  99.     sleepValue[kFGBusy] = 3;                    // Give the user the CPU's (almost) undivided attention
  100.     sleepValue[kBGBusy] = 60;                    // Process about one doc per second while in the background
  101. // BEGIN bug-fix 1.1.1 — Set a default value for sleepTime
  102.     sleepTime = 14;                            // We'll check reality later (in Start) — for now, assume we're
  103.                                             //    starting in the foreground (we know there's nothing to do yet)
  104. // END bug-fix 1.1.1
  105.     
  106.     running = TRUE;                            // Setting running = FALSE later will cause the dragon to quit
  107.     abortProcessing = FALSE;                    // Nothing's gone wrong yet
  108.     busyCursor = GetCursor (kWatchCursor);        // Get the watch cursor
  109.     if (busyCursor == NULL)                        // Make sure we actually got it
  110.         Abort (eInitializationFailed);                //    (not that this will ever happen, but…)
  111.     HLockHi ((Handle) busyCursor);                // Lock it down for carefree dereferencing later
  112.     cursorRgn = NULL;                            // This is for WaitNextEvent
  113.  
  114.     aeQueue = NULL;
  115.     numAEsPending = 0;
  116.     
  117.     autoQuit = FALSE;            // A subclass's constructor (which will be called AFTER this one) should set autoQuit
  118.                             //    to TRUE instead if it wants to hang around after starting up — and should set
  119.                             //    the corresponding bit in 'DrPr' 128
  120.     menusInstalled = FALSE;    // No menus yet
  121.     appleMenu = NULL;
  122.     fileMenu = NULL;
  123.     editMenu = NULL;
  124. }
  125.  
  126. void Dragon::Start (void)
  127. {
  128.     OSErr    err;
  129.     Boolean    inForeground;
  130.  
  131.     InitMem ();
  132.     InitMac ();
  133.     InitMilieu ();
  134.     InitPrefs ();
  135.     if (!autoQuit)                // Don't bother with menus if we're just going to quit right away
  136.         SetUpMenus ();
  137.     FlushEvents (everyEvent, 0);
  138.  
  139. // BEGIN bug-fix 1.1.1 — This was formerly done in Dragon::Dragon, but then inForeground was always being set to FALSE,
  140. //    even if the dragon was started in the foreground; consequently, drop-launch processing was slowed down considerably
  141.     // We hope to start in the foreground, but we may not — the Finder launches applications in the foreground, but
  142.     //    somebody else might launch us in the background.  Don't fight it, just grin and bear it.  If something goes
  143.     //    wrong, the safer assumption is that we're in the background
  144.     err = StartAppInForeground (&inForeground);
  145.     if (err != noErr)
  146.         inForeground = FALSE;
  147.         
  148.     runState = (inForeground ? kFGIdle : kBGIdle);    // We're idle until we get an 'odoc' event
  149.     sleepTime = sleepValue[runState];                // Make sure sleepTime is set correctly — sleepValue[0..3] will
  150.                                             //    have been set correctly by this time (namely, in InitPrefs)
  151. // END bug-fix
  152. }
  153.  
  154. void Dragon::Run (void)
  155. {
  156.     EventRecord    event;
  157.     
  158.     while (running) {
  159.         if (WaitNextEvent (everyEvent, &event, sleepTime, NULL))
  160.             DoEvent (&event);
  161.         else if (numAEsPending > 0)        // This code won't get called if we're busy, so we don't
  162.             ResumeAEvent ();            //    need to check for runState & maskBusy here
  163.         else
  164.             DoIdle ();
  165.     }
  166. }
  167.  
  168. void Dragon::Finish (void)
  169. {
  170.     if (preferences != NULL)
  171.         delete preferences;
  172.         
  173.     FlushAEventQueue ();
  174.     delete aeQueue;
  175.     
  176.     /* What else is there to do?
  177.         Release any temporary memory
  178.         Delete any temporary files
  179.         Remove any trap patches
  180.         etc. (look at CApplication.c in the THINK Class Library for some ideas)
  181.     */
  182. }
  183.  
  184. //    -------------------------------------------    I n i t i a l i z a t i o n     m e t h o d s    ----------------------------------------------
  185.  
  186. void Dragon::InitMem (void)
  187. {    
  188.     // If you want to increase the stack size, right here (before MaxApplZone) is where to do it
  189.     MaxApplZone ();
  190.     CallMoreMasters ();
  191. }
  192.  
  193. void Dragon::CallMoreMasters (void)
  194. {
  195.     short        mmCalls, **mmCallsHandle;
  196.     
  197.     // Call MoreMasters the number of times specified in the 'MoMa' resource — each call gives
  198.     //    us an extra block of master pointers
  199.     mmCallsHandle = (short **) GetResource ('MoMa', 128);
  200.     if (mmCallsHandle != NULL) {
  201.         for (mmCalls = **mmCallsHandle; mmCalls > 0; mmCalls--)
  202.             MoreMasters ();
  203.         HPurge ((Handle) mmCallsHandle);
  204.     }
  205. }
  206.  
  207. void Dragon::InitMac (void)
  208. {
  209.     InitGraf (&qd.thePort);    /* fhs 1994 july 1 */
  210.     InitFonts ();
  211.     InitWindows ();
  212.     InitMenus ();
  213.     TEInit ();
  214.     InitDialogs (NULL);
  215.     InitCursor ();
  216. }
  217.  
  218. void Dragon::InitMilieu (void)
  219. {
  220.     OSErr        err;
  221.     long            result;
  222.     Boolean        hasAppleEvents;
  223.     GstCheckList    **checkList;
  224.     
  225.     if (!TrapAvailable (0xA1AD))        // 0xA1AD == _Gestalt
  226.         Abort (eInsufficientSystem);
  227.     
  228.     checkList = (GstCheckList **) Get1Resource (kGstCheckType, rGstChecklist);
  229.     err = GestaltBatchCheck (checkList);
  230.     if (err != noErr)
  231.         Abort (err);
  232.     
  233.     HPurge ((Handle) checkList);
  234.     InitAppleEvents ();
  235. }
  236.  
  237. void Dragon::InitAppleEvents (void)
  238. {
  239.     OSErr    err;
  240.     
  241.     // Make the Apple event queue object, which stores suspended Apple events for handling later
  242.     aeQueue = MakeAEQObject ();
  243.     if (aeQueue == NULL)
  244.         Abort (memFullErr);
  245.     
  246.     // Install Apple event handler functions — abort if we get a non-zero result from AEInstallEventHandler
  247.     if (AEInstallEventHandler (kCoreEventClass, kAEOpenApplication, HandleOapp, 0, FALSE)
  248.         || AEInstallEventHandler (kCoreEventClass, kAEOpenDocuments, HandleOdoc, 0, FALSE)
  249.         || AEInstallEventHandler (kCoreEventClass, kAEPrintDocuments, HandlePdoc, 0, FALSE)
  250.         || AEInstallEventHandler (kCoreEventClass, kAEQuitApplication, HandleQuit, 0, FALSE)
  251.     )
  252.         Abort (eCouldntInstallAppleEvents);
  253. }
  254.  
  255. AppleEventQueue *Dragon::MakeAEQObject (void)
  256. {
  257.     return new AppleEventQueue;
  258. }
  259.  
  260. //    -------------------------------------------    P r o c e s s i n g     m e t h o d s    ----------------------------------------------
  261.  
  262. OSErr Dragon::ProcessDroppings (AEDescList *docList)
  263. {
  264.     // We've received an 'odoc' event and are ready to process the docs (files, folders and volumes) in the event
  265.     
  266.     // NOTE:    If you are upgrading from Dragonsmith 1.0b2, do NOT override this method! — read the documentation for
  267.     //        instructions (you need to override ProcessFile and/or ProcessDirectory instead)
  268.     
  269.     long            numDocs, i, actualSize;
  270.     Boolean        wasAlias;
  271.     OSErr        err;
  272.     AEKeyword    keyword;
  273.     DescType    returnedType;
  274.     
  275.     err = AECountItems (docList, &numDocs);
  276.     if (err == noErr) {
  277.         BeginProcessing ();
  278. // NOTE bug-fix 1.1.1 — added test of running to for-loop condition
  279.         for (i = 1; i <= numDocs && running && !abortProcessing; i++) {
  280.             err = AEGetNthPtr (docList, i, typeFSS, &keyword, &returnedType,
  281.                                                         (Ptr) curDocFSS, sizeof (FSSpec), &actualSize);
  282.             if (err == noErr) {
  283.                 err = FSpToPBCatInfo (curDocPB, curDocFSS, resolveAliases, followAliasChain, &wasAlias);
  284.                 if (err == noErr)
  285.                     ProcessDoc ();
  286.             }
  287.         }
  288.         err = noErr;
  289.         EndProcessing ();
  290.     }
  291.     
  292.     return err;
  293. }
  294.  
  295. void Dragon::BeginProcessing (void)
  296. {
  297.     // Override if you need to do anything special before processing any docs — for example, to interact with the user.
  298.     //    Remember, you can always bail out by calling StopProcessing
  299.     
  300.     // NOTE:    Your subclass's method should call inherited::BeginProcessing (or duplicate its functionality)
  301.     
  302.     // Default behavior —
  303.     //    1.    Turn on the busy flag in runState and adjust sleepTime accordingly
  304.     //    2.    Disable menus
  305.     //    3.    Put up a busy cursor
  306.     
  307.     curDirDepth = 0;                // We haven't yet delved down into any folders or disks
  308.     abortProcessing = FALSE;        // Nothing's gone wrong yet…
  309.     
  310.     runState |= maskBusy;
  311.     sleepTime = sleepValue[runState & (maskInBG | maskBusy)];
  312.     if (menusInstalled) {
  313.         AdjustMenusBusy ();
  314.         DrawMenuBar ();            // Disabling an entire menu requires us to call DrawMenuBar afterwards
  315.     }
  316.     CursorBusy ();
  317. }
  318.  
  319. void Dragon::EndProcessing (void)
  320. {
  321.     // Surely someone will think of something else to do here!
  322.  
  323.     // NOTE:    Your subclass's method should call inherited::EndProcessing
  324.     
  325.     // Default behavior —
  326.     //    1.    Turn on the busy flag in runState and adjust sleepTime accordingly
  327.     //    2.    Enable menus
  328.     //    3.    Restore the arrow cursor
  329.     
  330.     curDirDepth = 0;                // Reset, just to be cautious …
  331.     
  332.     runState &= ~maskBusy;
  333.     sleepTime = sleepValue[runState & (maskInBG | maskBusy)];    // Mask out any bits other than the bg and busy ones, for safety
  334.     if (menusInstalled) {
  335.         AdjustMenusIdle ();
  336.         DrawMenuBar ();            // Enabling an entire menu requires us to call DrawMenuBar afterwards
  337.     }
  338.     CursorIdle ();
  339. }
  340.  
  341. void Dragon::AdjustMenusBusy (void)
  342. {
  343.     // We're about to start processing docs — disable any menu items that should not be selected (generally speaking,
  344.     //    this means everything but the Apple and Edit menus)
  345.     
  346.     DisableItem (appleMenu, 1);        // Disable the About… item
  347.     DisableItem (fileMenu, 0);        // Disable the entire File menu
  348. }
  349.  
  350. void Dragon::AdjustMenusIdle (void)
  351. {
  352.     // Enable menu items that were disabled when we started processing docs
  353.     
  354.     EnableItem (appleMenu, 1);        // Enable the About… item
  355.     EnableItem (fileMenu, 0);        // Enable what we disabled in AdjustMenusBusy — this will NOT enable items that
  356.                                 //    were disabled to begin with
  357. }
  358.  
  359. void Dragon::CursorBusy (void)
  360. {
  361.     // Put up a cursor to show the user we're working on it (only if we're in the foreground)
  362.     if (!(runState & maskInBG))
  363.         SetCursor (*busyCursor);
  364. }
  365.  
  366. void Dragon::CursorIdle (void)
  367. {
  368.     // Restore the arrow cursor
  369.     InitCursor ();
  370. }
  371.  
  372. void Dragon::ShowProgress (void)
  373. {
  374.     // This is a good place to let the user know you're working on things — spin a cursor, show a progress bar (hard to
  375.     //    do if your dragon looks inside folders, I'm afraid), etc.
  376. }
  377.  
  378. void Dragon::DoBusy (void)
  379. {
  380.     // Override this to do periodic actions while we're processing docs — it'll be called once before each doc.  This is the
  381.     //    "busy" counterpart to DoIdle
  382.  
  383.     // Default behavior —
  384.     //    1.    Call ShowProgress to let the user know we're hard at work
  385.     //    2.    Call WaitNextEvent — yield some time to background processes, let the user cancel the docs processing,
  386.     //            switch to another process, or launch something from the Apple menu (if it exists), etc. Note that this
  387.     //            may result in an Apple event being generated (including 'quit', which lands us in StopRunning)
  388.     
  389.     EventRecord    event;
  390.     Boolean        canAbort;
  391.     
  392.     ShowProgress ();
  393.     
  394.     if (WaitNextEvent (everyEvent, &event, sleepTime, NULL))        // At this point, sleepTime should be a very low value
  395.         DoEvent (&event);
  396.     
  397.     // Note the absence of an else-clause here (as contrasted with the call to WaitNextEvent in the Run method) — we don't
  398.     //    want to call DoIdle here (after all, we're not idle, we're busy!)
  399. }
  400.  
  401. void Dragon::ProcessDoc (void)
  402. {
  403.     // If you override this method, make sure you call CanProcessDoc first and bail out if it returns FALSE
  404.     // You might, for example, want to treat volumes and folders differently — you could add a separate method
  405.     //    ProcessVolume that's called if curDocIsVolume == TRUE
  406.     
  407.     DoBusy ();
  408.     
  409.     // We have to check running and abortProcessing before continuing — DoBusy may call StopRunning
  410.     if (running && !abortProcessing && CanProcessDoc ()) {
  411.         if (curDocIsFile) {
  412.             if (curFileCreator == signature)
  413.                 ProcessOwnedFile ();
  414.             else
  415.                 ProcessFile ();
  416.         } else {
  417.             // It's a directory (either a folder or a volume) — look inside it only if we haven't already gone down as
  418.             //    far as we're allowed (remember, directory levels are zero or negative, never positive)
  419.             if (dirDepthLimit < curDirDepth) {
  420.                 curDirDepth--;        // We're going down a level
  421.                 ProcessDocsInDirectory (curDocPB->h.fileParam.ioVRefNum, curDocPB->h.fileParam.ioDirID);
  422.                 curDirDepth++;    // Pop back up to the level we were at before
  423.             } else if (!filesOnly)
  424.                 ProcessDirectory ();
  425.         }
  426.     }
  427. }
  428.  
  429. Boolean Dragon::CanProcessDoc (void)
  430. {
  431.     OSType    type;
  432.     
  433.     // We have to get a little tricky here — the macro curFileType is only valid for files
  434.     type = curDocIsFile ? curFileType : (curDocIsVolume ? 'disk' : 'fold');
  435.     if (OpenableType (type, acceptableTypes))
  436.         return useCustomFilter ? CustomFilterDoc () : TRUE;
  437.     else
  438.         return FALSE;
  439. }
  440.  
  441. Boolean Dragon::CustomFilterDoc (void)
  442. {
  443.     return TRUE;        // Default is to not filter out anything
  444. }
  445.  
  446. void Dragon::ProcessDocsInDirectory (short vRefNum, long dirID)
  447. {
  448.     // NOTE:    The way things are set up in Dragon, this method is called only by ProcessDoc.  If your dragon calls it from one
  449.     //        of its methods, make sure you set up curDirDepth (and dirDepthLimit) correctly beforehand!
  450.     
  451.     // Be very careful when overriding this method — things are set up here to make sure that the values in
  452.     //    *curDocPB and *curDocFSS are valid any time another method might be called
  453.     
  454.     OSErr        err;
  455.     short        i;
  456.     Boolean        wasAliasFile;
  457.         
  458.     // NOTE:    curDocPB->h.hFileInfo.ioNamePtr was set to curDocFSS->name in Dragon::Dragon
  459.     //        above and would only need to be reset if it was changed at some point later on
  460.     
  461.     // Loop through each file/folder in a directory
  462.     for (i = 1, err = noErr; err != fnfErr && !abortProcessing; i++) {
  463.     
  464.         // The loop ends only when we get an fnfErr — any other error simply causes us to skip to the next iteration
  465.         //    (i.e., the next file/folder in the given folder/volume)
  466.         
  467.         // Set up the three fields that together identify the file or folder we want information about — they're
  468.         //    liable to have changed since the last time we were at this point in the loop
  469.         curDocPB->h.fileParam.ioVRefNum = vRefNum;
  470.         curDocPB->h.fileParam.ioDirID = dirID;
  471.         curDocPB->h.fileParam.ioFDirIndex = i;
  472.         
  473.         // Convert curDocPB to curDocFSS, filling in curDocPB and (if appropriate) resolving aliases along the way
  474.         err = PBToFSpCatInfo (curDocPB, curDocFSS, resolveAliases, followAliasChain, &wasAliasFile);
  475.         if (err == noErr)
  476.             ProcessDoc ();
  477.     }
  478.     
  479.     // According to Tech Note #68: Searching All Directories on an HFS Volume, "… stopp[ing] the whole search when
  480.     //    we [encounter] an error … cause[s] trouble with privilege errors on AppleShare volumes"
  481.     // This would seem to indicate we have to set abortProcessing = FALSE here (in case it was set to TRUE somewhere
  482.     //    along the line) — but it doesn't make sense to me, I don't like it, and I won't do it!
  483. }
  484.  
  485. void Dragon::ProcessFile (void)
  486. {
  487.     // NOTE:    This method, if overridden, can do ANYTHING to *curDocFSS and *curDocPB, UNLESS you've
  488.     //        done some indiscreet overriding of Dragon::ProcessDroppings or Dragon::ProcessDocsInDirectory
  489.     //        You do NOT have to restore any values in *curDocFSS or *curDocPB!!!
  490.  
  491.     // Of course, you will normally override this method
  492. }
  493.  
  494. void Dragon::ProcessDirectory (void)
  495. {
  496.     // NOTE:    This method, if overridden, can do ANYTHING to *curDocFSS and *curDocPB, UNLESS you've
  497.     //        done some indiscreet overriding of Dragon::ProcessDroppings or Dragon::ProcessDocsInDirectory
  498.     //        You do NOT have to restore any values in *curDocFSS or *curDocPB!!!
  499.  
  500.     // The directory ID of the folder or volume we're processing is in the .ioDirID field
  501. }
  502.  
  503. void Dragon::ProcessOwnedFile (void)
  504. {
  505.     // NOTE:    This method will be called whenever a file created by this dragon is dropped on it or double-clicked.
  506.     //        This is a handy way to change preferences quickly (if one is sneaky, it can be done in the middle of a
  507.     //        dropped batch)
  508.     
  509.     if (curFileType == prefsFileType)
  510.         if (preferences->UseFile (curDocFSS))
  511.             ReadPrefs ();
  512. }
  513.  
  514. void Dragon::StopProcessing (OSErr err)
  515. {
  516.     // Your class should override this method if it wants to beep, display an error message, etc. — just make sure your
  517.     //    method calls inherited::StopProcessing
  518.     
  519.     abortProcessing = TRUE;
  520. }
  521.  
  522. void Dragon::SaveDocInfo (Boolean refreshFinder)
  523. {
  524.     /* Call this method right after you change one or more of the following values —
  525.     
  526.                 curFileType    curFileCreator        curFileFlags    curDocCreated    curDocModified
  527.                 
  528.     or any other field in *curDocPB that PBSetCatInfo takes as input.  To rename the current document, you have to call
  529.     PBHRename rather than just changing *curDocName
  530.     
  531.     WARNING:    If your dragon makes any low-level calls (PB____) that change any other values in *curDocPB that
  532.                 PBSetCatInfo takes as input, then it should do so AFTER calling this method, or save and restore
  533.                 those values, or refrain from calling UpdateCatInfo at all.  For a PARTIAL list of low-level File Manager
  534.                 calls that leave garbage in fields that PBSetCatInfo uses, see the Dragonsmith Programmer's Manual
  535.     */
  536.  
  537.     long        ioDirIDWas;
  538.     short    ioFDirIndexWas;
  539.     OSErr    err;
  540.     
  541.     // Save field values
  542.     ioDirIDWas = curDocPB->c.hFileInfo.ioDirID;        // File number or directory ID, returned by PBGetCatInfo
  543.     ioFDirIndexWas = curDocPB->c.hFileInfo.ioFDirIndex;        
  544.     
  545.     // Give them correct values for PBSetCatInfo
  546.     curDocPB->c.hFileInfo.ioDirID = curDocPB->c.hFileInfo.ioFlParID;
  547.     curDocPB->c.hFileInfo.ioFDirIndex = 0;
  548.     
  549.     // Call PBSetCatInfo for the current document
  550.     err = PBSetCatInfoSync ((CInfoPBPtr) curDocPB);
  551.     
  552.     // Restore the original values
  553.     curDocPB->c.hFileInfo.ioDirID = ioDirIDWas;
  554.     curDocPB->c.hFileInfo.ioFDirIndex = ioFDirIndexWas;
  555.     
  556.     // Refresh the Finder's display of the current document, if desired — we use the FSpRefreshFinderDisplay
  557.     //    function from FileUtils.c rather than duplicating its functionality here
  558.  
  559.     if (refreshFinder && err == noErr)
  560.         FSpRefreshFinderDisplay (curDocFSS);
  561. }
  562.  
  563. //    -------------------------------------------    E v e n t s     m e t h o d s    ----------------------------------------------
  564.  
  565. void Dragon::DoEvent (EventRecord *event)
  566. {
  567.     switch (event->what) {
  568.         case mouseDown:
  569.             DoMouseDown (event);
  570.             break;
  571.         case mouseUp:
  572.             DoMouseUp (event);
  573.             break;
  574.         case keyDown:
  575.         case autoKey:
  576.             DoKeyDown (event);
  577.             break;
  578.         case activateEvt:
  579.             if (event->modifiers & activeFlag)
  580.                 DoActivate (event);
  581.             else
  582.                 DoDeactivate (event);
  583.             break;
  584.         case updateEvt:
  585.             DoUpdateEvent (event);
  586.             break;
  587.         case diskEvt:
  588.             DoDiskInsert (event);
  589.             break;
  590.         case osEvt:                // Suspend, resume, and mouse-moved events
  591.             DoOSEvent (event);
  592.             break;
  593.         case kHighLevelEvent:
  594.             DoHighLevelEvent (event);
  595.             break;
  596.     }
  597. }
  598.  
  599. void Dragon::DoMouseDown (EventRecord *theEvent)
  600. {
  601.     WindowPtr    whichWindow;
  602.     short        domain;
  603.     long            menuItemCode;
  604.     
  605.     domain = FindWindow (theEvent->where, &whichWindow);
  606.     switch (domain) {
  607.         case inSysWindow:
  608.             SystemClick (theEvent, whichWindow);
  609.             break;
  610.         case inMenuBar:
  611.             // Watch out for stray mouseDowns in non-existent menus …
  612.             if (menusInstalled) {
  613.                 menuItemCode = MenuSelect (theEvent->where);
  614.                 if (menuItemCode > 0x0000FFFF) {
  615.                     DoMenu (menuItemCode);
  616.                     ShowMenuAction ();
  617.                 }
  618.             }
  619.             break;
  620.         default:
  621.             break;
  622.     }
  623. }
  624.  
  625. void Dragon::DoMouseUp (EventRecord *theEvent)
  626. {
  627.     // Do nothing
  628. }
  629.  
  630. void Dragon::DoKeyDown (EventRecord *theEvent)
  631. {
  632.     long        menuItemCode;
  633.     
  634.     if (runState & maskBusy && IsCancelEvent (theEvent))
  635.         StopProcessing (userCanceledErr);
  636.     else if ((theEvent->modifiers & cmdKey) && menusInstalled) {        // If we have menus, handle
  637.         menuItemCode = MenuKey (theEvent->message);            //     cmd-key combos
  638.         if (menuItemCode > 0x0000FFFF) {                            // The high word of menuItemCode will be
  639.             DoMenu (menuItemCode);                            //    zero (and the low word undefined) if
  640.             ShowMenuAction ();                                //    no menu item was chosen
  641.         }
  642.     }
  643. }
  644.  
  645. void Dragon::DoActivate (EventRecord *theEvent)
  646. {
  647.     // Null method — override if you have windows
  648. }
  649.  
  650. void Dragon::DoDeactivate (EventRecord *theEvent)
  651. {
  652.     // Null method — override if you have windows
  653. }
  654.  
  655. void Dragon::DoUpdateEvent (EventRecord *theEvent)
  656. {
  657.     // Null method — override if you have windows
  658. }
  659.  
  660. void Dragon::DoDiskInsert (EventRecord *theEvent)
  661. {
  662.     Point    pt = {100, 100};        // Would { -1, -1 } give us a centered dialog??
  663.     
  664.     if ((short) (theEvent->message >> 16) != noErr)        // If the disk isn't formatted (or has problems),
  665.         (void) DIBadMount (pt, theEvent->message);    //     give the user the opportunity to initialize it
  666. }
  667.  
  668. void Dragon::DoOSEvent (EventRecord *theEvent)
  669. {
  670.     switch ((theEvent->message >> 24) & 0x00FF) {        // High byte tells us what kind of event it is
  671.         case suspendResumeMessage:            
  672.             if (theEvent->message & resumeFlag)
  673.                 DoResume ();
  674.             else
  675.                 DoSuspend ();
  676.             break;
  677.         case mouseMovedMessage:
  678.             break;
  679.         default:
  680.             break;
  681.     }
  682. }
  683.  
  684. void Dragon::DoHighLevelEvent (EventRecord *theEvent)
  685. {
  686.     OSErr    err;
  687.     
  688.     // Don't assume all high level events are Apple Events
  689.     if (((HLEventPtr) theEvent)->eventClass == kCoreEventClass)
  690.         err = AEProcessAppleEvent (theEvent);
  691.     else
  692.         DoOtherHLEvent (theEvent);
  693.  
  694.             //  NOTE:    When running in the THINK C Debugger, you have to be careful not to set any
  695.             //        breakpoints between the call to WaitNextEvent and the call to AEProcessAppleEvent
  696.             //        which dispatches to the Apple event handlers (HandleOdoc etc.).  If you forget this,
  697.             //        the call to AEProcessAppleEvent will return noOutstandingHLE (== -608)
  698.  
  699. }
  700.  
  701. void Dragon::DoOtherHLEvent (EventRecord *theEvent)
  702. {
  703.     // Subclasses may want to handle other high-level events
  704. }
  705.  
  706. void Dragon::DoSuspend (void)
  707. {
  708.     // Turn on the in-background bit in runState and adjust sleepTime accordingly
  709.     runState |= maskInBG;
  710.     sleepTime = sleepValue[runState & (maskInBG | maskBusy)];
  711.     
  712.     // If we're busy, stop the busy cursor (boring watch, spinning cursor, back-flipping dogcow, whatever)
  713.     if (runState & maskBusy)
  714.         CursorIdle ();
  715. }
  716.  
  717. void Dragon::DoResume (void)
  718. {
  719.     // Turn off the in-background bit in runState and adjust sleepTime accordingly
  720.     runState &= ~maskInBG;
  721.     sleepTime = sleepValue[runState & (maskInBG | maskBusy)];
  722.     
  723.     // If we're busy, put the "busy" cursor back up
  724.     if (runState & maskBusy)
  725.         CursorBusy ();
  726. }
  727.  
  728. void Dragon::DoIdle (void)
  729. {
  730.     // Override this method to do periodic actions while we're sitting around waiting for something to happen.  This is the
  731.     //    "idle" counterpart to DoBusy (which is called for each doc we try to process) — it'll never be called while we're
  732.     //    processing docs
  733. }
  734.  
  735. void Dragon::StopRunning (OSErr err)
  736. {
  737.     if (runState & maskBusy)            // If we're processing docs, we have to set abortProcessing = TRUE or
  738.         StopProcessing (eForcedQuit);    //    else we'll blithely continue to process 'em (bad idea!)
  739.     running = FALSE;
  740. }
  741.  
  742. //    -------------------------------------------    M e n u     m e t h o d s    ----------------------------------------------
  743.  
  744. void Dragon::SetUpMenus (void)
  745. {
  746.     // NOTE:    Your sub-class of Dragon probably won't need to call SetUpMenus from "outside" (meaning anywhere
  747.     //        but in an overridden SetUpMenus method).  If it does, make sure you DON'T call it unless menusInstalled
  748.     //        == FALSE.  Otherwise, you'll have extra menus (or a cute little picture of a bomb…)
  749.     
  750.     // Override this method if you have any menus in addition to the Apple, File, and Edit menus
  751.     // Make sure your method calls inherited::SetUpMenus at the beginning and DrawMenuBar at the end, so that —
  752.     //    1.    Dragon's menus get initialized
  753.     //    2.    The menu bar gets drawn
  754.  
  755.     appleMenu = GetMenu (mApple);        // Standard Apple Menu
  756.     AddResMenu (appleMenu, 'DRVR');
  757.     InsertMenu (appleMenu, 0);
  758.     
  759.     fileMenu = GetMenu (mFile);
  760.     InsertMenu (fileMenu, 0);
  761.     
  762.     editMenu = GetMenu (mEdit);
  763.     InsertMenu (editMenu, 0);
  764.     
  765.     DrawMenuBar ();
  766.     menusInstalled = TRUE;
  767. }
  768.  
  769. void Dragon::DoMenu (long menuItemCode)
  770. {
  771.     // Override this method if you need more than just the Apple, File, and Edit menus
  772.     
  773.     short    menuID, itemNum;
  774.  
  775.     menuID = menuItemCode >> 16;
  776.     itemNum = menuItemCode & 0xFFFF;
  777.  
  778.     switch (menuID) {
  779.         case mApple:
  780.             DoAppleMenu (itemNum);
  781.             break;
  782.         case mFile:
  783.             DoFileMenu (itemNum);
  784.             break;
  785.         case mEdit:
  786.             DoEditMenu (itemNum);
  787.             break;
  788.         default:
  789.             break;
  790.     }
  791. }
  792.  
  793. void Dragon::DoAppleMenu (short itemNum)
  794. {
  795.     // Implement standard Apple menu functionality
  796.     
  797.     Str255    itemStr;
  798.     
  799.     if (itemNum == iAbout)
  800.         DoAbout ();
  801.     else {
  802.         GetItem (appleMenu, itemNum, itemStr);
  803.         OpenDeskAcc (itemStr);
  804.     }
  805. }
  806.  
  807. void Dragon::DoAbout (void)
  808. {
  809.     // Override this method if you want an About… window
  810. }
  811.  
  812. void Dragon::DoFileMenu (short itemNum)
  813. {
  814.     if (itemNum == CountMItems (fileMenu))        // This will work fine no matter how many items you have
  815.                                         //    in your File menu, as long as Quit is at the end
  816. // BEGIN bug-fix 1.1.1 — Trigger a 'quit' event instead of just setting a flag
  817.         (void) SendToSelf (kCoreEventClass, kAEQuitApplication, NULL, NULL, kAEAlwaysInteract | kAENoReply);
  818. // END bug-fix 1.1.1
  819. }
  820.  
  821. void Dragon::DoEditMenu (short itemNum)
  822. {
  823.     // SystemEdit does undo, cut, copy, paste, and clear for us
  824.     if (itemNum <= 6)
  825.         (void) SystemEdit (itemNum - 1);
  826. }
  827.  
  828. OSErr Dragon::DoOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  829. {
  830.     // Override this method if you want your dragon to do something special when it's launched
  831.     //    by double-click in the Finder (like put up a status window, start a directory scan, etc.)
  832.     
  833.     if (autoQuit)            // Should we quit immediately?
  834.         StopRunning (eNormalQuit);
  835.  
  836.     return noErr;
  837. }
  838.  
  839. OSErr Dragon::DoOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  840. {
  841.     // You should't need to override this method — it calls ProcessDroppings with info gleaned from the 'odoc' event
  842.     
  843.     register OSErr        err;
  844.     AEDescList        docList;
  845.     static AEHandlerFunc     theOdocHander = NewAEEventHandlerProc( HandleOdoc );     /* fhs 1994 july 1 */
  846.     
  847.     // If we're already processing an Apple event, call SuspendAEvent instead
  848.     if (runState & maskBusy)
  849.         return SuspendAEvent (theAppleEvent, theReply, theOdocHander, refcon);     /* fhs 1994 july 1 */
  850.     
  851.     err = AEGetParamDesc (theAppleEvent, keyDirectObject, typeAEList, &docList);
  852.     if (err == noErr) {
  853.         err = GotRequiredParams (theAppleEvent);
  854.         if (err == noErr)
  855.             err = ProcessDroppings (&docList);
  856.         (void) AEDisposeDesc (&docList);
  857.     }
  858.         
  859.     if (autoQuit)                // Should we quit immediately after handling this event?
  860.         StopRunning (eNormalQuit);
  861.         
  862.     return err;
  863. }
  864.  
  865. OSErr Dragon::SuspendAEvent (AppleEvent *event, AppleEvent *reply, AEHandlerFunc handler, long refcon)     /* fhs 1994 july 1 */
  866. {
  867.     // Postpone processing of the current Apple event till we're done with the current one (and any previously
  868.     //    postponed ones)
  869.     
  870.     OSErr    err;
  871.     
  872.     err = aeQueue->Put (event, reply, handler, refcon);
  873.     if (err == noErr) {
  874.         numAEsPending++;
  875.         err = AESuspendTheCurrentEvent (event);
  876.     }
  877.     
  878.     return err;
  879. }
  880.  
  881. void Dragon::ResumeAEvent (void)
  882. {
  883.     AppleEvent        event, reply;
  884.     AEHandlerFunc    handler;
  885.     long                refcon;
  886.     OSErr            err;
  887.     
  888.     err = aeQueue->Get (&event, &reply, &handler, &refcon);
  889.     if (err == noErr && handler != NULL)
  890.         err = AEResumeTheCurrentEvent (&event, &reply, handler, refcon);
  891.         
  892.     if (--numAEsPending < 0)
  893.         numAEsPending = 0;
  894. }
  895.  
  896. void Dragon::FlushAEventQueue (void)
  897. {
  898.     AppleEvent        event, reply;
  899.     AEHandlerFunc    handler;
  900.     long                refcon;
  901.     OSErr            err;
  902.     
  903.     for (err = noErr; err != noOutstandingHLE; ) {
  904.         err = aeQueue->Get (&event, &reply, &handler, &refcon);
  905.         if (err == noErr && handler != NULL)
  906.             // "Resume" here means we call ReturnEventNotHandled, which just returns errAEEventNotHandled
  907.             (void) AEResumeTheCurrentEvent (&event, &reply, &ReturnEventNotHandled, refcon);
  908.     }
  909. }
  910.  
  911. OSErr Dragon::DoPdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  912. {
  913.     if (autoQuit)                        // Should we quit immediately after handling this event?
  914.         StopRunning (eNormalQuit);
  915.         
  916.     return errAEEventNotHandled;        // Most subclasses won't need to override this method
  917. }
  918.  
  919. OSErr Dragon::DoQuit (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  920. {
  921.     StopRunning (eNormalQuit);            // This'll keep us from going through the main event loop again
  922.     return noErr;
  923. }
  924.  
  925. //    -------------------------------------------    P r e f e r e n c e s     m e t h o d s    ----------------------------------------------
  926.  
  927. void Dragon::InitPrefs (void)
  928. {
  929.     FSSpec    prefsFile;
  930.     
  931.     // NOTE:    The application's resource fork must be the most-recently-used one at this
  932.     //        point — you won't have to worry about this unless you do some heavy-duty
  933.     //        overriding of Dragon methods (::InitPrefs, ::Start, ::Dragon)
  934.     // TIP:    Always treat Get1Resource and Count1Resources (etc.) with great respect!
  935.     
  936.     preferences = MakePrefsObject ();
  937.     if (preferences == NULL)
  938.         Abort (memFullErr);
  939.     else {
  940.         if (!preferences->Init (appResFork, signature, prefsFileType))
  941.             Abort (eInitializationFailed);                // Abort if preferences initialization failed completely
  942.         if (FindPrefsFile (&prefsFile))
  943.             (void) preferences->UseFile (&prefsFile);    // Ignore the returned value — we'll call ReadPrefs regardless
  944.         ReadPrefs ();
  945.     }
  946. }
  947.  
  948. void Dragon::ReadPrefs (void)
  949. {
  950.     // Read in any needed prefs resources
  951.     // If your subclass overrides this method, it must call inherited::ReadPrefs
  952.     // When overriding, check for NULL handles — do the same in any other method that might rely on a prefs
  953.     //    resource being in memory.  A screwed-up preferences file should never cause big problems (i.e., crash).
  954.     //    You'll notice that this is the only method in this file that uses the dragonPrefs instance variable — the only
  955.     //    reason it's provided as an instance variable is so that a subclass can change the Dragon preferences in
  956.     //    the preferences files it uses
  957.  
  958.     // Note that you should NOT reference any preferences resource handles that may have been in memory
  959.     //    — they will not be valid handles now because the file they were from has by this time been closed
  960.     
  961.     short    flags, i;
  962.  
  963.     if (dragonPrefs != NULL)
  964.         preferences->ReleasePrefResource (prefDragonPrefs);
  965.         
  966.     dragonPrefs = (DragonPrefsRec **) preferences->GetPrefResource (prefDragonPrefs);
  967.     if (dragonPrefs != NULL) {
  968.         // Read Boolean settings from the miscFlags field of the Dragon preferences resource
  969.         flags = (*dragonPrefs)->miscFlags;
  970.         filesOnly = flags & maskFilesOnly;
  971.         resolveAliases = flags & maskResolveAliases;
  972.         followAliasChain = flags & maskFollowAliasChain;
  973.         autoQuit = flags & maskAutoQuit;
  974.         
  975.         // Read the 4 possible sleepTime values from the Dragon preferences resource
  976.         for (i = 0; i <= 3; i++)
  977.             sleepValue[i] = (*dragonPrefs)->sleep[i];
  978.         sleepTime = sleepValue[runState & (maskInBG | maskBusy)];
  979.         
  980.         // Read dirDepthLimit from the Dragon preferences resource
  981.         dirDepthLimit = (*dragonPrefs)->depthLim;
  982.     }
  983. }
  984.  
  985. Preferences *Dragon::MakePrefsObject (void)
  986. {
  987.     // Override this method if you write your own subclass of Preferences and want to use it instead
  988.     return new Preferences;
  989. }
  990.  
  991. Boolean Dragon::FindPrefsFile (FSSpec *fss)
  992. {
  993.     OSErr            err;
  994.     Str63            fileName;
  995.     unsigned short    len;
  996.     Handle            h = NULL;
  997.     short            saveResFork;
  998.     
  999.     saveResFork = CurResFile ();
  1000.     UseResFile (appResFork);
  1001.     h = Get1Resource ('STR ', rPrefsFileName);
  1002.     UseResFile (saveResFork);
  1003.     if (h != NULL) {
  1004.         len = **((unsigned char **) h);
  1005.         if (len != 0 && len <= 63) {    
  1006.             HLock (h);
  1007.             CopyPStr ((unsigned char *) *h, fss->name);
  1008.             HUnlock (h);
  1009.             HPurge (h);
  1010.     
  1011.             fss->vRefNum = appFile.vRefNum;                    // First look in the directory that the application is in
  1012.             fss->parID = appFile.parID;
  1013.             if (IsPrefsFile (fss))
  1014.                 return TRUE;
  1015.                 
  1016.             err = FSpFindFolder (kPreferencesFolderType, fss);    // Then look in the Preferences folder
  1017.             if (IsPrefsFile (fss))                                // If there's a preferences file there,
  1018.                 return TRUE;                                //    then we're done
  1019.             else                                            // Otherwise,
  1020.                 return MakePrefsFile (fss);                    //    we create one there
  1021.         }
  1022.     }
  1023.     
  1024.     if (h != NULL)
  1025.         HPurge (h);
  1026.     
  1027.     return FALSE;            // If we encountered any problems, then we'll rely on the app file for our preferences resources
  1028. }
  1029.  
  1030. Boolean Dragon::MakePrefsFile (FSSpec *fss)
  1031. {
  1032.     OSErr    err;
  1033.     
  1034.     FSpCreateResFile (fss, signature, prefsFileType, smSystemScript);
  1035.     return (ResError () == noErr);
  1036. }
  1037.  
  1038. Boolean Dragon::IsPrefsFile (FSSpec *fss)
  1039. {
  1040.     // This method returns TRUE if the file designated by fss is a preferences file for this dragon
  1041.     // NOTE:    IsPrefsFile may alter *fss, since we tell FSpToPBCatInfo to resolve aliases.  This is a feature, not a bug!
  1042.  
  1043.     OSErr        err;
  1044.     Boolean        isFolder, wasAliasFile;
  1045.     PBRecUnion    pb;
  1046.     Str63        name;
  1047.     
  1048.     pb.c.hFileInfo.ioCompletion = NULL;
  1049.     pb.c.hFileInfo.ioNamePtr = (StringPtr) &name;
  1050.     
  1051.     err = FSpToPBCatInfo (&pb, fss, TRUE, TRUE, &wasAliasFile);
  1052.     return (err == noErr && PBIsFile (&pb) && PBFileCreator (&pb) == signature && PBFileType (&pb) == prefsFileType);
  1053. }
  1054.  
  1055. //    -------------------------------------------    M i s c e l l a n e o u s    ----------------------------------------------
  1056.  
  1057. Boolean Dragon::InteractWithUser (long timeOut)
  1058. {
  1059.     // Call this method any time during the handling of an Apple event (I don't know what will happen
  1060.     //    if there's no current Apple event) if you need to come to the foreground (to pose a dialog, etc.)
  1061.     
  1062.     return AEInteractWithUser (timeOut, NULL, &CallWaitIdle) == noErr;
  1063. }
  1064.  
  1065. Boolean Dragon::WaitIdle (EventRecord *theEvent, long *sleep, RgnHandle *mouseRgn)
  1066. {
  1067.     // This method is called whenever we get an event while AEInteractWithUser is in process
  1068.     // The system will give us only null, OS, activate, and deactivate events
  1069.     
  1070.     *sleep = sleepTime;            // These two assignments tell AEInteractWithUser what to
  1071.     *mouseRgn = cursorRgn;    //    pass to WaitNextEvent on our behalf
  1072.     
  1073.     DoEvent (theEvent);
  1074.     
  1075.     return FALSE;                // Don't abort AEInteractWithUser unless it times out
  1076. }
  1077.  
  1078. void Dragon::Abort (short errNum)
  1079. {
  1080.     Error (errNum);
  1081.     ExitToShell ();
  1082. }
  1083.  
  1084. void Dragon::Error (short errNum)
  1085. {
  1086.     Str255    string;
  1087.     
  1088.     NumToString (errNum, string);
  1089.     ParamText (string, "\p", "\p", "\p");
  1090.     Alert (rErrorAlert, NULL);
  1091. }
  1092.  
  1093. //    -------------------------------------------    F u n c t i o n s            ----------------------------------------------
  1094.  
  1095. pascal OSErr HandleOapp (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  1096. {
  1097.     return gDragon->DoOapp (theAppleEvent, theReply, refcon);
  1098. }
  1099.  
  1100. pascal OSErr HandleOdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  1101. {
  1102.     return gDragon->DoOdoc (theAppleEvent, theReply, refcon);
  1103. }
  1104.  
  1105. pascal OSErr HandlePdoc (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  1106. {
  1107.     return gDragon->DoPdoc (theAppleEvent, theReply, refcon);
  1108. }
  1109.  
  1110. pascal OSErr HandleQuit (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  1111. {
  1112.     return gDragon->DoQuit (theAppleEvent, theReply, refcon);
  1113. }
  1114.  
  1115. pascal OSErr ReturnEventNotHandled (AppleEvent *theAppleEvent, AppleEvent *theReply, long refcon)
  1116. {
  1117.     return errAEEventNotHandled;
  1118. }
  1119.  
  1120. pascal Boolean CallWaitIdle (EventRecord *theEvent, long *sleep, RgnHandle *mouseRgn)
  1121. {
  1122.     // Idle function to be used as parameter to AEInteractWithUser
  1123.     
  1124.     return gDragon->WaitIdle (theEvent, sleep, mouseRgn);
  1125. }
  1126.  
  1127.